/**
 * @file msg_com.c
 * @author your name (you@domain.com)
 * @brief pub and sub communication mode
 * @version 0.1
 * @date 2023-05-28
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#include "msg_com.h"


static void MsgMutexLock(MutexLock lock, bool is_lock)
{
    if(lock)
    {
       lock(is_lock);
    }
}

int MsgCenterLoop(MsgCenterHandle_t *msg_handle)
{ 
    //MSG_CENTER_LOG("EventProcess start \r\n");
    MsgCenterHandle_t *handle = (MsgCenterHandle_t *)msg_handle;
    if(handle == NULL)
    {
       MSG_CENTER_LOG("handle is null"); 
       return -1;
    }

	if (!handle->exit_flg)
    {
        EventPublishNode_t cur = {0};
        /*find last valid publish event*/
        MsgMutexLock(handle->mutexPublish, true);
        EventPublishNode_t **curPublish = &handle->eventPublishHead;
        EventPublishNode_t *tempPublish = NULL;
        /*curPublish = last_node->next pointer address, so modify *curPublish = modify last_node->next obj*/
        while ((*curPublish) != NULL )
        {
            if(strlen((*curPublish)->event) > 0)
            {
                if((*curPublish)->next == NULL)
                {
					//copy current pub node to cut obj, data is copy pointer, release in end
                    memcpy(&cur, *curPublish, sizeof(EventPublishNode_t));
                    break;
                }
 
                memcpy(&cur, *curPublish, sizeof(EventPublishNode_t));
                /*save the next node of valid cur node*/
                tempPublish = (*curPublish)->next;
                break;
            }
            curPublish = &((*curPublish)->next);
        }
        
        if(curPublish != NULL && *curPublish != NULL)
        {
            memset((*curPublish)->event, 0, sizeof((*curPublish)->event));
            free((*curPublish));
            *curPublish = NULL;
            //delete cur node, and use the next node replace
            if(tempPublish)
            {
               *curPublish = tempPublish;
            }
        }   
        MsgMutexLock(handle->mutexPublish, false);
		
        if (strlen(cur.event) <= 0) 
        {
			if(cur.data)
			{ 
				free(cur.data);
			}  			
            return 0;
        }
		
        MsgMutexLock(handle->mutexSubscriber, true);
        EventSubscriberNode_t *curSubscriber = (EventSubscriberNode_t *)handle->eventSubscriberHead;
        while (curSubscriber != NULL)
        {
            if(curSubscriber->needId == cur.srcId && strcmp(curSubscriber->event , cur.event) == 0)
            {
				int data_len = 0;
				memcpy((void *)&data_len, cur.data, 4);
                curSubscriber->func(cur.event, cur.data + 4, data_len, cur.srcId);
            }
			else if(curSubscriber->needId == ANY_ID && strcmp(curSubscriber->event , cur.event) == 0)
			{
				int data_len = 0;
				memcpy((void *)&data_len, cur.data, 4);				
				curSubscriber->func(cur.event, cur.data + 4, data_len, ANY_ID);
			}
            curSubscriber = curSubscriber->next;
        }
        MsgMutexLock(handle->mutexSubscriber, false);

        if(cur.data)
        { 
            free(cur.data);
        }   
    }

    //MSG_CENTER_LOG("EventProcess end \r\n");
	return 0;
}


MsgCenterHandle_t *MsgCenterInit(MutexLock mutexSubscriber, MutexLock mutexPublish)
{
	MsgCenterHandle_t *handle = (MsgCenterHandle_t *)malloc(sizeof(MsgCenterHandle_t));
	if(handle == NULL)
	{
		MSG_CENTER_LOG("hanlde malloc fail\r\n");
		return NULL;
	}

	handle->exit_flg = 0;
	
    handle->eventSubscriberHead = (EventSubscriberNode_t *)malloc(sizeof(EventSubscriberNode_t));
	if(handle->eventSubscriberHead == NULL)
	{
	    MSG_CENTER_LOG("eventSubscriberHead malloc fail\r\n");
		goto FAIL;
	}
	
    memset(handle->eventSubscriberHead->event, 0, sizeof(handle->eventSubscriberHead->event));
    handle->eventSubscriberHead->func = NULL;
    handle->eventSubscriberHead->next = NULL;
    
    handle->eventPublishHead = (EventPublishNode_t *)malloc(sizeof(EventPublishNode_t));
	if(handle->eventPublishHead == NULL)
	{
	    MSG_CENTER_LOG("eventSubscriberHead malloc fail\r\n");
		goto FAIL;
	}
	
    memset(handle->eventPublishHead->event, 0, sizeof(handle->eventPublishHead->event));
    handle->eventPublishHead->next = NULL;
    handle->eventPublishHead->data = NULL;
    
    handle->mutexSubscriber = mutexSubscriber;
    handle->mutexPublish = mutexPublish;
	
	return handle;
    FAIL:
	if(handle->eventSubscriberHead)
	{
		free(handle->eventSubscriberHead);
		handle->eventSubscriberHead = NULL;
	}
	
	if(handle->eventPublishHead)
	{
		free(handle->eventPublishHead);
		handle->eventPublishHead = NULL;
	}
	
	return NULL;
}


void MsgCenterDeinit(MsgCenterHandle_t **msg_handle)
{

	MsgCenterHandle_t *handle = *msg_handle;
	if(handle == NULL)
	{
	   MSG_CENTER_LOG("handle is null\r\n");
	   return;
	}
	
	MsgMutexLock(handle->mutexSubscriber, true);
	EventSubscriberNode_t *subCur =  handle->eventSubscriberHead;
	EventSubscriberNode_t *subNext = subCur->next;
	while(subCur)
	{
		free(subCur);
		subCur = subNext;
		if(subCur)
		   subNext = subCur->next;
	}
	handle->eventSubscriberHead = NULL;
	MsgMutexLock(handle->mutexSubscriber, false);


	MsgMutexLock(handle->mutexPublish, true);
	EventPublishNode_t *pubCur =  handle->eventPublishHead;
	EventPublishNode_t *pubNext = pubCur->next;
	while(pubCur)
	{
		if(pubCur->data)
		{
			free(pubCur->data);
		}
		free(pubCur);
		pubCur = pubNext;
		if(pubCur)
		   pubNext = pubCur->next;
	}
	handle->eventPublishHead = NULL;
	MsgMutexLock(handle->mutexPublish, false);
    handle->exit_flg = 1;
    free(*msg_handle);
	*msg_handle = NULL;
	return;
}


int SubscriberEvent(MsgCenterHandle_t *handle, char * event, HandleEvent func, int needId)
{
	if(handle == NULL || func == NULL || event == NULL)
	{
	   MSG_CENTER_LOG("handle|func|event is null\r\n");
	   return -1;
	}
	
	EventSubscriberNode_t *node = (EventSubscriberNode_t *)malloc(sizeof(EventSubscriberNode_t));
	if(node == NULL)
	{
	   MSG_CENTER_LOG("node is malloc fail\r\n");
	   return -1;
	}
	
	node->func = func;
	node->needId = needId;
	strncpy(node->event, event, sizeof(node->event));
    node->next = NULL;
	 
	MsgMutexLock(handle->mutexSubscriber, true);
    EventSubscriberNode_t *current = handle->eventSubscriberHead;
	if(current != NULL)
	{
	    while (current->next != NULL)
	    {
	        current = current->next;
			if(strcmp(current->event, event) == 0 && current->func == func && current->needId == needId)
		    {
		       MSG_CENTER_LOG("exist same node %s\r\n", event);
		       free(node);
			   MsgMutexLock(handle->mutexSubscriber, false);
			   return -1;
			}
	    }
	    current->next = node;
	}
	else
	{
        free(node);
	    MsgMutexLock(handle->mutexSubscriber, false);
	    return -1;
	}
    MsgMutexLock(handle->mutexSubscriber, false);
	return 0;
}


int ReleaseSubscriberEvent(MsgCenterHandle_t *handle, char *event, HandleEvent func)
{
	if(handle == NULL || event == NULL || func == NULL)
	{
	   MSG_CENTER_LOG("handle|func|event is null\r\n");
	   return -1;
	}
	
	MsgMutexLock(handle->mutexSubscriber, true);
    EventSubscriberNode_t **current = &handle->eventSubscriberHead;
    while ((*current) != NULL)
    {
        if(strcmp((*current)->event, event) == 0 && (*current)->func == func)
        {
            if((*current)->next == NULL)
            {
                (*current)->func = NULL;
				free(*current);
                (*current) = NULL;
                break;
            }
            EventSubscriberNode_t *de = *current;
            *current = (*current)->next;
            free(de);
            break;
        }
        current = &(*current)->next;
    }
    MsgMutexLock(handle->mutexSubscriber, false);
	
	return 0;
	
}


int PublishEvent(MsgCenterHandle_t *handle, char * event, void *data, int datalen, int srcId)
{
    if(handle == NULL || event == NULL)
	{
	   MSG_CENTER_LOG("handle|event is null\r\n");
	   return -1;
	}
	
	EventPublishNode_t *node = (EventPublishNode_t *)malloc(sizeof(EventPublishNode_t));
	if(node == NULL)
	{
	   MSG_CENTER_LOG("node is malloc fail\r\n");
	   return -1;
	}
	
	node->srcId = srcId;
	strncpy(node->event, event, sizeof(node->event));

    //usr data first 4 bytes set data len
    node->data = malloc(datalen + 4);
    if(node->data == NULL)
    {
        MSG_CENTER_LOG("node->data is malloc fail\r\n");
        free(node);
        return -1;
    }
    
    memcpy(node->data, (void *)&datalen, 4);
	memcpy(node->data + 4, data, datalen);
    node->next = NULL;
	
	MsgMutexLock(handle->mutexPublish, true);
    EventPublishNode_t *current = handle->eventPublishHead;
	if(current != NULL)
	{
	    while (current->next != NULL)
	    {
	        current = current->next;
	    }
	    current->next = node;
	}
	else
	{
		free(node->data);
        free(node);
		MsgMutexLock(handle->mutexPublish, false);
		return -1;
	}
    MsgMutexLock(handle->mutexPublish, false);
	return 0;
} 

